#!/usr/bin/env python

"""The edit_text_layer attributes widget."""


import wx
# if we don't have log.py, don't crash
try:
    import log
    log = log.Log('pyslip.log', log.Log.DEBUG)
except ImportError:
    def log(*args, **kwargs):
        pass


################################################################################
# An 'attributes' widget
################################################################################

myEVT_CHANGEATTR = wx.NewEventType()
EVT_CHANGEATTR = wx.PyEventBinder(myEVT_CHANGEATTR, 1)

class ChangeAttrEvent(wx.PyCommandEvent):
    """Event sent when the attributes are changed."""
        
    def __init__(self, eventType, id, attributes):
        wx.PyCommandEvent.__init__(self, eventType, id)
        self.attributes = attributes
                                                                                                
class Attributes(wx.Panel):
    # legal placement strings
    PlaceList = ['cc', 'nw', 'cn', 'ne', 'ce', 'se', 'cs', 'sw', 'cw']

    # GridBagSizer pack border value
    Border = 1

    # colour of text field 'in error'
    TextFieldErrorColour = '#ffcccc'

    # label layout flags
    LabelFlags = wx.TOP|wx.BOTTOM|wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL
    CtlFlags = wx.TOP|wx.BOTTOM|wx.EXPAND
    TxtFlags = 0 # wx.TE_RIGHT

    # various colours
    TextFieldErrorColour = '#ffcccc'
    TextFieldOKColour = wx.WHITE

    # default attribute values
    DefaultLonValue = ''
    DefaultLatValue = ''
    DefaultLabelValue = ''
    DefaultPlaceValue = 'cc'
    DefaultXOffset = 0
    DefaultYOffset = 0
    DefaultColourValue = wx.BLACK
    DefaultSizeValue = 10


    def __init__(self, parent):
        """Initialize widget.

        parent   reference to owning widget
        appcfg  reference to application config object
        """

        wx.Panel.__init__(self, parent, wx.ID_ANY)
        self.SetBackgroundColour(wx.WHITE)
        
        # create GUI objects
        box = wx.StaticBox(self, wx.ID_ANY, ' Text Attributes ')
        sbs = wx.StaticBoxSizer(box, orient=wx.VERTICAL)
        gbs = wx.GridBagSizer(hgap=2, vgap=2)

        # longitude/latitude
        lab_lon = wx.StaticText(self, wx.ID_ANY, 'Lon:', style=self.TxtFlags)
        self.txt_lon = wx.TextCtrl(parent=self, id=wx.ID_ANY, value='')
        lab_lon.SetToolTip(wx.ToolTip('Longitude of text point'))
        self.txt_lon.SetToolTip(wx.ToolTip('Longitude of text point'))
        lab_lat = wx.StaticText(self, wx.ID_ANY, ' Lat:', style=self.TxtFlags)
        self.txt_lat = wx.TextCtrl(parent=self, id=wx.ID_ANY, value='')
        lab_lat.SetToolTip(wx.ToolTip('Latitude of text point'))
        self.txt_lat.SetToolTip(wx.ToolTip('Latitude of text point'))

        gbs.Add(lab_lon, pos=(0,0), border=self.Border, flag=self.LabelFlags)
        gbs.Add(self.txt_lon, pos=(0,1), border=self.Border, flag=self.CtlFlags)
        gbs.Add(lab_lat, pos=(0,2), border=self.Border, flag=self.LabelFlags)
        gbs.Add(self.txt_lat, pos=(0,3), border=self.Border, flag=self.CtlFlags)

        # spacer
        lab_label = wx.StaticText(self, wx.ID_ANY, 'Label:', style=self.TxtFlags)
        self.txt_label = wx.TextCtrl(parent=self, id=wx.ID_ANY, value='', size=wx.DefaultSize)
        lab_label.SetToolTip(wx.ToolTip('Label of text point'))
        self.txt_label.SetToolTip(wx.ToolTip('Label of text point'))

        gbs.Add(lab_label, (1,0), border=self.Border, flag=self.LabelFlags)
        gbs.Add(self.txt_label, (1,1), span=(1,3), border=self.Border, flag=self.CtlFlags)

        # add the placement control
        place_text = wx.StaticText(self, wx.ID_ANY, 'Placement:', style=self.TxtFlags)
        self.cb_place = wx.ComboBox(self, -1, '', (15, 30), wx.DefaultSize, self.PlaceList,
                                    wx.CB_DROPDOWN|wx.CB_READONLY)
        self.cb_place.SetValue(self.PlaceList[0])
        place_text.SetToolTip(wx.ToolTip('Placement of label with respect to point'))
        self.cb_place.SetToolTip(wx.ToolTip('Placement of label with respect to point'))

        gbs.Add(place_text, pos=(2,0), border=self.Border, flag=self.LabelFlags)
        gbs.Add(self.cb_place, pos=(2,1), span=(1,3), border=self.Border, flag=self.CtlFlags)

        # add the text offset attributes
        xoffset_text = wx.StaticText(self, wx.ID_ANY, 'Offset x:', style=self.TxtFlags)
        self.spc_xoffset = wx.SpinCtrl(self, -1, '', (30, 20), (80, -1))
        self.spc_xoffset.SetRange(-1000, 1000)
        self.spc_xoffset.SetValue(0)
        yoffset_text = wx.StaticText(self, wx.ID_ANY, ' y:', style=self.TxtFlags)
        self.spc_yoffset = wx.SpinCtrl(self, -1, '', (30, 20), (80, -1))
        self.spc_yoffset.SetRange(-1000, 1000)
        self.spc_yoffset.SetValue(0)
        xoffset_text.SetToolTip(wx.ToolTip('X offset of label with respect to point'))
        self.spc_xoffset.SetToolTip(wx.ToolTip('X offset of label with respect to point'))
        yoffset_text.SetToolTip(wx.ToolTip('Y offset of label with respect to point'))
        self.spc_yoffset.SetToolTip(wx.ToolTip('Y offset of label with respect to point'))

        gbs.Add(xoffset_text, pos=(3,0), border=self.Border, flag=self.LabelFlags)
        gbs.Add(self.spc_xoffset, pos=(3,1), border=self.Border, flag=self.CtlFlags)
        gbs.Add(yoffset_text, pos=(3,2), border=self.Border, flag=self.LabelFlags)
        gbs.Add(self.spc_yoffset, pos=(3,3), border=self.Border, flag=self.CtlFlags)

        # add the text colour attribute
        txt_label = wx.StaticText(self, wx.ID_ANY, 'Colour:', style=self.TxtFlags)
        self.cp_colour = wx.ColourPickerCtrl(self, id=wx.ID_ANY, col=wx.BLACK)
        self.cp_colour.SetInternalMargin(1)
        txt_label.SetToolTip(wx.ToolTip('Text colour'))
        self.cp_colour.SetToolTip(wx.ToolTip('Text colour'))

        gbs.Add(txt_label, pos=(4,0), border=self.Border, flag=self.LabelFlags)
        gbs.Add(self.cp_colour, pos=(4,1), span=(1,3), border=self.Border, flag=self.CtlFlags)

        # add the text size attributes
        txt_label = wx.StaticText(self, wx.ID_ANY, 'Font size:', style=self.TxtFlags)
        self.spc_size = wx.SpinCtrl(self, -1, '', (30, 20), (80, -1))
        self.spc_size.SetRange(0, 100)
        self.spc_size.SetValue(0)
        txt_label.SetToolTip(wx.ToolTip('Label font size'))
        self.spc_size.SetToolTip(wx.ToolTip('Label font size'))

        gbs.Add(txt_label, pos=(5,0), border=self.Border, flag=self.LabelFlags)
        gbs.Add(self.spc_size, pos=(5,1), span=(1,3), border=self.Border, flag=self.CtlFlags)

        # add the SAVE button
        self.btn_Save = wx.Button(self, wx.ID_ANY, label='Save')
        #self.btn_Save.Disable()
        gbs.Add(self.btn_Save, pos=(6,1), span=(1,2), border=self.Border, flag=self.LabelFlags)

        # finish layout
        sbs.Add(gbs)
        self.SetSizer(sbs)
        sbs.Fit(self)

        # set handler for SAVE button
        self.btn_Save.Bind(wx.EVT_BUTTON, self.OnSave)

    def OnSave(self, event):
        """Validate attribute values and create event."""

        # validate attribute values
        (error, lon) = self.ValidateLon(False)
        (error, lat) = self.ValidateLat(error)

        if error:
            return

        # get all other values (can't be in error)
        lab = self.txt_label.GetValue()
        place = self.cb_place.GetValue()
        off_x = self.spc_xoffset.GetValue()
        off_y = self.spc_yoffset.GetValue()
        colour = self.cp_colour.GetColour()
        size = self.spc_size.GetValue()

        # create 'changed' event, use pyslip attribute names
        attributes = {'lon': lon, 'lat': lat, 'label': lab,
                      'placement': place, 'offset_x': off_x, 'offset_y': off_y,
                      'colour': colour, 'fontsize': size}

        # raise a change event
        event = ChangeAttrEvent(myEVT_CHANGEATTR, self.GetId(), attributes)
        self.GetEventHandler().ProcessEvent(event)

    def ValidateLon(self, error=False):
        """Validate the longitude value."""

        try:
            lon = float(self.txt_lon.GetValue())
            assert 0.0 <= lon <= 360.0
        except (AssertionError, ValueError):
            error = True
            lon = None
            self.txt_lon.SetBackgroundColour(self.TextFieldErrorColour)
        else:
            self.txt_lon.SetBackgroundColour(self.TextFieldOKColour)

        self.txt_lon.Update()

        return (error, lon)

    def ValidateLat(self, error=False):
        """Validate the latitude value."""

        try:
            lat = float(self.txt_lat.GetValue())
            assert -90.0 <= lat <= 90.0
        except (AssertionError, ValueError):
            error = True
            lat = None
            self.txt_lat.SetBackgroundColour(self.TextFieldErrorColour)
        else:
            self.txt_lat.SetBackgroundColour(self.TextFieldOKColour)

        self.txt_lat.Update()

        return (error, lat)

    def SetActive(self, state):
        """Set widget active state."""

        self.active = state
        if state:
            self.Enable()
        else:
            self.Disable()

    def Update(self, state, data):
        """Set attribute values.

        state  final state of the control (True or False)
        data   a dictionary of state values:
                   {'lon': lon, 'lat': lat, 'lab': lab,
                    'placement': place, 'off_x': off_x, 'off_y': off_y,
                    'colour': colour, 'size': size}
        """

        # update attribute values
        self.state = False      # inhibit creating events during update

        # set values from 'data' dictionary
        self.txt_lon.SetValue(data.get('lon', self.DefaultLonValue))
        self.txt_lat.SetValue(data.get('lat', self.DefaultLatValue))
        self.txt_label.SetValue(data.get('lab', self.DefaultLabelValue))
        self.cb_place.SetValue(data.get('place', self.DefaultPlaceValue))
        self.spc_xoffset.SetValue(data.get('off_x', self.DefaultXOffset))
        self.spc_yoffset.SetValue(data.get('off_y', self.DefaultYOffset))
        self.cp_colour.SetColour(data.get('colour', self.DefaultColourValue))
        self.spc_size.SetValue(data.get('size', self.DefaultSizeValue))

        # set final state
        self.SetActive(state)


if __name__ == '__main__':
    import sys
    import traceback
    import tkinter_error

    import log
    log = log.Log('editor.log', log.Log.DEBUG)


    class MyFrame(wx.Frame):
        def __init__(self):
            wx.Frame.__init__(self, None, wx.ID_ANY, 'Attribute Widget', size=(300,300))
            panel = wx.Panel(self, wx.ID_ANY)

            # build the GUI
            self.AttrCtrl = Attributes(panel)

            data =  {'lon': str(123.0), 'lat': str(-10.5), 'lab': 'test',
                     'place': 'ne', 'off_x': 1, 'off_y': 2,
                     'colour': wx.RED, 'size': 9}
            self.AttrCtrl.Update(True, data)

            self.AttrCtrl.Bind(EVT_CHANGEATTR, self.OnChange)

        def OnChange(self, event):
            log('Change event attributes: %s' % str(event.attributes))


    # our own handler for uncaught exceptions
    def excepthook(type, value, tb):
        msg = '\n' + '=' * 80
        msg += '\nUncaught exception:\n'
        msg += ''.join(traceback.format_exception(type, value, tb))
        msg += '=' * 80 + '\n'
        log(msg)
        tkinter_error.tkinter_error(msg)
        sys.exit(1)

    # plug our handler into the python system
    sys.excepthook = excepthook

    # start wxPython app
    app = wx.PySimpleApp()
    frame = MyFrame()
    frame.Show(True)

#    import wx.lib.inspection
#    wx.lib.inspection.InspectionTool().Show()

    app.MainLoop()
                                                                      

